
#include <assert.h>
#include <stdlib.h>
#include "utility.h"
#include "qr-const.h"
#include "qr-common.h"
#include "qr-list.h"

static int qr_list_append_padding(QRList* qr_list);

QRList* qr_list_new(int version, ECLevel ec_level)
{
	QRList* qr_list;

	if(qr_check_version(version) < 0 || qr_check_ec_level(ec_level) < 0)
	{
		RES_NULL("Argument is invalid.");
	}

	qr_list = (QRList*)malloc(sizeof(QRList));
	if(NULL == qr_list)
	{
		RES_NULL("Invoke malloc error.");
	}
	qr_list->_version = version;
	qr_list->_ec_level = ec_level;
	qr_list->_head = NULL;
	qr_list->_tail = NULL;
	qr_list->_encoded = NULL;
	return qr_list;
}

void qr_list_destroy(QRList* qr_list)
{
	QRElement* iter, * qr_element;

	if(qr_list != NULL)
	{
		bit_stream_destroy(qr_list->_encoded);
		iter = qr_list->_head;
		while(iter != NULL)
		{
			qr_element = iter;
			iter = qr_element->_next;
			qr_element_destroy(qr_element);
		}
	}
}

int qr_list_append(QRList* qr_list, QRElement* qr_element)
{
	if(NULL == qr_list || NULL == qr_element)
	{
		RES_INVAL("Argument is invalid.");
	}
	if(qr_element->_next != NULL)
	{
		RES_INVAL("Argument is invalid.");
	}
	if(qr_list->_encoded != NULL)
	{
		RES_INVAL("Argument is invalid.");
	}

	if(qr_list->_tail != NULL)
	{
		assert(NULL == qr_list->_tail->_next);
		qr_list->_tail->_next = qr_element;
		qr_list->_tail = qr_element;
	}
	else
	{
		assert(NULL == qr_list->_head);
		qr_list->_head = qr_element;
		qr_list->_tail = qr_element;
	}
	return 0;
}

unsigned char* qr_list_encode(QRList* qr_list)
{
	QRElement* iter;
	int version;
	BitStream* encoded;
	BitStream* combined;

	if(NULL == qr_list)
	{
		RES_NULL("Argument is invalid.");
	}
	if(qr_list->_encoded != NULL)
	{
		RES_NULL("Argument is invalid.");
	}

	iter = qr_list->_head;
	assert(iter != NULL);
	version = qr_list->_version;
	assert(0 == qr_check_version(version));
	encoded = qr_element_encode(iter, version);
	if(NULL == encoded)
	{
		RES_NULL("Invoke qr_element_encode error.");
	}
	iter = iter->_next;
	while(iter != NULL)
	{
		combined = qr_element_encode(iter, version);
		if(NULL == combined)
		{
			bit_stream_destroy(encoded);
			RES_NULL("Invoke qr_element_encode error.");
		}
		if(bit_stream_append(encoded, combined) < 0)
		{
			bit_stream_destroy(combined);
			bit_stream_destroy(encoded);
			RES_NULL("Invoke bit_stream_append error.");
		}
		bit_stream_destroy(combined);
		iter = iter->_next;
	}
	qr_list->_encoded = encoded;
	if(qr_list_append_padding(qr_list) < 0)
	{
		bit_stream_destroy(qr_list->_encoded);
		qr_list->_encoded = NULL;
		RES_NULL("Invoke qr_list_append_padding error.");
	}
	return bit_stream_convert_to_bytes(qr_list->_encoded);
	// TODO: Delete member "_encoded" whether useless.
//	bit_stream_destroy(qr_list->_encoded);
//	qr_list->_encoded = NULL;
}


int qr_list_append_padding(QRList* qr_list)
{
	int max_data_words, max_data_bits;
	int bits, words;
	int padding_length;
	unsigned char* padding;
	int i;

	if(NULL == qr_list)
	{
		RES_INVAL("Argument is invalid.");
	}
	if(qr_check_version(qr_list->_version) < 0 || qr_check_ec_level(qr_list->_ec_level) || NULL == qr_list->_encoded)
	{
		RES_INVAL("Argument is invalid.");
	}

	max_data_words = qr_data_words_count(qr_list->_version, qr_list->_ec_level);
	if(max_data_words < 0)
	{
		RES_INVAL("Invoke qr_data_words_count error.");
	}
	max_data_bits = max_data_words << 3;
	bits = qr_list->_encoded->_length;
	assert(bits <= max_data_bits);
	if(bits == max_data_bits)
	{
		return 0;
	}
	else if(max_data_bits - bits < 5)
	{
		if(bit_stream_append_num(qr_list->_encoded, max_data_bits - bits, 0) < 0)
		{
			RES_INVAL("Invoke bit_stream_append_num error.");
		}
	}
	else
	{
		bits += 4;
		words = (bits + 7) >> 3;
		if(bit_stream_append_num(qr_list->_encoded, 4 + (words << 3) - bits, 0) < 0)
		{
			RES_INVAL("Invoke bit_stream_append_num error.");
		}
		padding_length = max_data_words - words;
		if(padding_length > 0)
		{
			padding = (unsigned char*)malloc(padding_length * sizeof(unsigned char));
			if(NULL == padding)
			{
				RES_INVAL("Invoke malloc error.");
			}
			for(i = 0; i < padding_length; ++i)
			{
				padding[i] = (0 == (i % 2)) ? 0xec : 0x11;
			}
			if(bit_stream_append_bytes(qr_list->_encoded, padding_length, padding) < 0)
			{
				free(padding);
				RES_INVAL("Invoke bit_stream_append_bytes error.");
			}
			free(padding);
		}
	}
	return 0;
}

